package com.unitd.modules.configcenter.component;

import com.unitd.frame.comm.utils.SimpleCryptUtils;
import com.unitd.frame.comm.utils.encrypt.RSA;
import com.unitd.modules.comm.exception.ConfigServerException;
import com.unitd.modules.configcenter.entity.AppSecret;
import com.unitd.modules.configcenter.service.IAppSecretService;
import com.unitd.frame.comm.utils.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;

import java.io.FileInputStream;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.PublicKey;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

/**
 * @desc 自定义组件, 用于对配置中心配置的app进行加解密
 * @filename CryptComponent.java
 * @copyright www.unitd.com
 * @author Hudan
 * @version 1.0
 * @date 2018-01-30
 */
@Component
public class CryptComponent implements EnvironmentAware {

	@Autowired
	private IAppSecretService appSecretService;

	/** RSA 的公私密钥对 */
	private static Map<String, PublicKey> rsaKeyPairs = new HashMap<>();
	/** 密钥存储对象 */
	private KeyStore keyStore;
	/** 进行DES加解密后的密位的前缀 */
	private static final String DES_PREFIX = "{Cipher}";
	/** 进行RSA加解密后的密位的前缀 */
	private static final String RSA_PREFIX = "{Cipher:RSA}";
	/** 密钥存储对想初始化错误信息 */
	private static String keyStoreInitErrorMsg;


	/**
	 * @desc 根据查询条件获取app相应的密钥信息
	 * @param appId 应用主键ID
	 * @param env app所属环境
	 * @param encryptType 加密类型
	 * @return app的密钥信息
	 */
	public AppSecret getAppSecret(String appId, String env, String encryptType) {

		AppSecret entity = appSecretService.selectByBean(new AppSecret(appId, env, encryptType));

		if (entity == null && "DES".equals(encryptType)) {
			entity = new AppSecret();
			entity.setAppId(appId);
			entity.setEnv(env);
			entity.setSecretType(encryptType);
			entity.setSecretKey(UUID.randomUUID().toString().replaceAll("-", ""));
			appSecretService.insertSelective(entity);
		}
		return entity;
	}

	/**
	 * @desc 根据应用密钥管理信息, 获取该密钥的公钥信息
	 * @param appSecret 密钥信息
	 * @return 公钥
	 */
	private PublicKey getRsaPublicKey(AppSecret appSecret) {
		if (keyStore == null)
			throw new ConfigServerException(10, "无RSA私钥配置-" + keyStoreInitErrorMsg);
		String key = appSecret.getEnv() + appSecret.getAppId();
		PublicKey rsaKey = rsaKeyPairs.get(key);
		if (rsaKey == null) {
			synchronized (rsaKeyPairs) {
				String keyPassword = SimpleCryptUtils.decrypt(appSecret.getSecretKey(), appSecret.getSecretPass());
				rsaKey = RSA.loadPublicKeyFromKeyStore(keyStore, appSecret.getSecretKey(), keyPassword);
				rsaKeyPairs.put(key, rsaKey);
			}
		}
		return rsaKey;
	}

	/**
	 * @desc 根据密钥对指定的数据进行加密
	 * @param appSecret 密钥对象
	 * @param data 需要进行加密的数据
	 * @return 加密后的密文
	 */
	public String encode(AppSecret appSecret, String data) {
		if ("RSA".equalsIgnoreCase(appSecret.getSecretType())) {
			return RSA_PREFIX + RSA.encryptByPublicKey(getRsaPublicKey(appSecret), data);
		} else {
			return DES_PREFIX + SimpleCryptUtils.encrypt(appSecret.getSecretKey(), data);
		}
	}

	/**
	 * @desc 重写Spring的设置运行环境方法,获取当前应用对于密钥管理的配置西南西
	 * @param environment Spring运行环境对象
	 */
	@Override
	public void setEnvironment(Environment environment) {
		// keyStore 文件所在位置
		String location = environment.getProperty("cc.encrypt.keyStore.location");
		// 默认的keyStore 类型
		String storeType = environment.getProperty("cc.encrypt.keyStore.type", "JCEKS");
		// keyStore的解密密钥
		String storePass = environment.getProperty("cc.encrypt.keyStore.password");
		System.out.println("cc.encrypt.keyStore.location:" + location);

		if (StringUtils.isNotBlank(location)) {
			try {
				keyStore = KeyStore.getInstance(storeType);
				InputStream is = new FileInputStream(location);
				keyStore.load(is, storePass.toCharArray());
			} catch (Exception e) {
				keyStore = null;
				keyStoreInitErrorMsg = e.getMessage();
				e.printStackTrace();
			}
		}
	}
}